/*
    用户级线程(tid) + 内核级线程(LWP) = Linux中的线程

    用户级线程(Linux线程)：内核级线程 = 1:1
    每一个线程栈都放在线程动态库中。

    线程是具有独立栈结构的。而不是私有栈。。。完全可以通过主线程获取新线程栈内的变量，只是不这么干！
    线程和线程之间，几乎没有秘密。线程栈上的数据，也是可以被其他线程看到并且访问的。

    全局变量是被所有的线程同时看到并且访问的。
    但是如果想要一个私有的全局变量呢？线程局部存储原理TLS(thread local storage)
        ：在定义的全局变量前面加上__thread（编译器提供的）。此时每个线程都有自己私有的全局变量。
        ，__thread只能定义内置类型，不能用来修饰自定义类型。
        ，
*/


#include <iostream>
#include <pthread.h>
#include <vector>
#include <string>
#include <unistd.h>

using namespace std;

#define NUM 3

int g_val = 10; // 共享资源
__thread int g_val1 = 10; // __thread是编译器提供的，在编译的时候就默认把此全局变量给每一个线程开辟一份。
__thread int number = 0;

int *p = nullptr;

struct threadData
{
    // string tid;
    string thread_name;
};

string toHex(pthread_t tid)
{
    char buffer[128];
    snprintf(buffer, sizeof(buffer), "0x%x", tid);
    return  buffer;
}

// 所有的线程，执行的都是这一个函数！
void* threadRoutinue(void* args)
{   
    // 每个线程都有自己独立的test_i变量。因为每个线程都会执行这个函数，会在共享区创建自己的栈帧。
    int test_i = 0;
    // td就是当前线程的独立堆。当然也可以访问其他线程的堆。
    threadData *td = static_cast<threadData*>(args);

    // 此时可以不用频繁使用系统接口了，number是局部存储。拿到的都是自己的。
    // number = pthread_self();

    // 主线程获取新线程栈内部的变量 -- 完全Ok!
    if(td->thread_name == "thread-2") p = &test_i;
    int i = 0;
    while(i++ < 10)
    {
        cout << "pid: " << getpid() << ", tid: " << toHex(pthread_self()) << ", threadname: " 
            << td->thread_name << ",test_i: " << test_i << ",&test_i: " << &test_i << endl;
        sleep(1);
        test_i++;
    }

    delete td;
    return nullptr;
}


void InitThreadData(threadData* td, int number)
{
    td->thread_name = "thread-" + to_string(number);
}

int main()
{
    // 创建多线程
    vector<pthread_t> tids;
    for(int i = 0; i < NUM; ++i)
    {
        // 这样写不行， 因为此时的td在主栈区，出了for循环就销毁了，但是后边pthread_create传参还需要
        // threadData td;
        // td.thread_name = ;
        // td.tid = ;
        
        pthread_t tid;
        
        // 这里说明堆被所有线程访问。
        threadData *td = new threadData;
        InitThreadData(td, i);

        pthread_create(&tid, nullptr, threadRoutinue, td);
        tids.push_back(tid);
        sleep(1);
    }

    sleep(1);
    cout << "main thread get a thread local val, val: " << *p << ", &val: " << p << endl;
    for(int i = 0; i < NUM; ++i)
    {
        pthread_join(tids[i], nullptr);
    }
    return 0;
}